package avicit.bdp.dgs.metadata.service;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.service.dto.BdpColumnHistoryDTO;
import avicit.bdp.common.service.dto.BdpTableHistoryDTO;
import avicit.bdp.common.service.dto.ColumnPartitionedHistoryDTO;
import avicit.bdp.common.service.dto.CommonTypeDTO;
import avicit.bdp.common.service.service.CommonTypeService;
import avicit.bdp.core.enums.DataStateEnum;
import avicit.bdp.dgs.metadata.dao.MetadataBusinessDao;
import avicit.bdp.dgs.metadata.dao.MetadataTableDao;
import avicit.bdp.dgs.metadata.dao.MetadataTableHistoryDao;
import avicit.bdp.dgs.metadata.vo.ColumnCompareResponseVo;
import avicit.bdp.dgs.metadata.vo.MetadataTableColumnPartitionedVo;
import avicit.bdp.dgs.metadata.vo.MetadataTableColumnVo;
import avicit.bdp.dgs.metadata.vo.MetadataTableVo;
import avicit.bdp.dgs.metadata.vo.TablePropertiesCompareResponseVo;
import avicit.bdp.dgs.metadata.vo.TableVersionCompareResponseVo;
import avicit.bdp.dgs.metadata.vo.ValueCompareResponseDetail;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.exception.DaoException;
import avicit.platform6.core.rest.msg.QueryRespBean;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * MetadataTableDbmsService: 表类型元数据关系型数据库存储服务
 *
 * @author mayanj
 * @date 2023/06/02
 */
@Transactional(rollbackFor = Exception.class)
@Service
public class MetadataTableHistoryService
    extends BaseService<MetadataTableHistoryDao, BdpTableHistoryDTO> {

  @Resource private CommonTypeService commonTypeService;
  @Resource private MetadataTableColumnHistoryService metadataTableColumnHistoryService;

  @Resource
  private MetadataTableColumnPartitionHistoryService metadataTableColumnPartitionHistoryService;

  @Resource private MetadataTableDao metadataTableDao;
  @Resource private MetadataBusinessDao metadataBusinessDao;

  public boolean batchUpdate(List<BdpTableHistoryDTO> updateHistoryList) {
    return this.mapper.batchUpdate(updateHistoryList) > 0;
  }

  @Transactional(rollbackFor = Exception.class)
  public void deleteByTableId(String tableId) {
    if (StringUtils.isEmpty(tableId)) {
      throw new BusinessException("删除失败！传入的参数主键为null");
    }
    try {
      BdpTableHistoryDTO bdpTableHistoryDTO = new BdpTableHistoryDTO();
      bdpTableHistoryDTO.setTableId(tableId);
      delete(bdpTableHistoryDTO);
    } catch (Exception e) {
      throw new BusinessException(e.getMessage(), e);
    }
  }

  public int countByStatus(String tableId, int status) {
    return mapper.countByStatus(tableId, status);
  }

  public QueryRespBean<BdpTableHistoryDTO> getVersionPage(
      String tableId, Integer pageNo, Integer pageSize) {
    QueryRespBean<BdpTableHistoryDTO> queryRespBean = new QueryRespBean<>();
    PageHelper.startPage(pageNo, pageSize);
    Page<BdpTableHistoryDTO> page = this.mapper.getVersionPage(tableId);
    for (BdpTableHistoryDTO dto : page) {
      dto.setStatusString(DataStateEnum.getDescByCode(dto.getStatus()));
    }
    queryRespBean.setResult(page);
    return queryRespBean;
  }

  public MetadataTableVo queryHistoryById(String id) {
    BdpTableHistoryDTO tableHistoryDTO = this.selectByPrimaryKey(id);
    Objects.requireNonNull(tableHistoryDTO, "数据不存在");
    if (StringUtils.isNotBlank(tableHistoryDTO.getTypeId())) {
      CommonTypeDTO commonTypeDTO =
          commonTypeService.selectByPrimaryKey(tableHistoryDTO.getTypeId());
      if (commonTypeDTO != null) {
        tableHistoryDTO.setTypeName(commonTypeDTO.getName());
      }
    }
    handleHistoryTableInfo(tableHistoryDTO);
    return getMetadataTableVo(tableHistoryDTO);
  }

  private MetadataTableVo getMetadataTableVo(BdpTableHistoryDTO bdpTableHistoryDTO) {
    MetadataTableVo tableVo = new MetadataTableVo();

    tableVo.setId(bdpTableHistoryDTO.getId());
    tableVo.setCatalogId(bdpTableHistoryDTO.getTypeId());
    tableVo.setCatalogName(bdpTableHistoryDTO.getTypeName());
    tableVo.setName(bdpTableHistoryDTO.getName());
    tableVo.setCnName(bdpTableHistoryDTO.getCnName());
    tableVo.setDataSourceId(bdpTableHistoryDTO.getDataSourceId());
    tableVo.setDataSourceName(bdpTableHistoryDTO.getDataSourceName());
    tableVo.setDataSourceType(bdpTableHistoryDTO.getDataSourceType());
    tableVo.setStatus(bdpTableHistoryDTO.getStatus());
    tableVo.setTableVersion(bdpTableHistoryDTO.getTableVersion());

    List<BdpColumnHistoryDTO> columnList = bdpTableHistoryDTO.getColumnHistoryList();
    if (CollectionUtils.isNotEmpty(columnList)) {
      List<MetadataTableColumnVo> columnVoList = new ArrayList<>();
      for (BdpColumnHistoryDTO bdpColumnDTO : columnList) {
        MetadataTableColumnVo columnVo = new MetadataTableColumnVo();
        BeanUtils.copyProperties(bdpColumnDTO, columnVo);
        columnVo.setTableMetaId(bdpColumnDTO.getTableId());
        columnVoList.add(columnVo);
      }
      tableVo.setColumnList(columnVoList);
    }

    List<ColumnPartitionedHistoryDTO> columnPartitionedHistorList =
        bdpTableHistoryDTO.getColumnPartitionedHistoryList();
    if (CollectionUtils.isNotEmpty(columnPartitionedHistorList)) {
      List<MetadataTableColumnPartitionedVo> columnPartitionedList = new ArrayList<>();
      for (ColumnPartitionedHistoryDTO columnPartitionedHistoryDTO : columnPartitionedHistorList) {
        MetadataTableColumnPartitionedVo columnPartitionedVo =
            new MetadataTableColumnPartitionedVo();
        BeanUtils.copyProperties(columnPartitionedHistoryDTO, columnPartitionedVo);
        columnPartitionedVo.setTableMetaId(columnPartitionedHistoryDTO.getTableId());
        columnPartitionedList.add(columnPartitionedVo);
      }
      tableVo.setColumnPartitionedList(columnPartitionedList);
    }

    return tableVo;
  }

  private void handleHistoryTableInfo(BdpTableHistoryDTO tableHistoryDTO) {
    if (tableHistoryDTO != null) {
      BdpColumnHistoryDTO columnHistoryDTO = new BdpColumnHistoryDTO();
      columnHistoryDTO.setTableId(tableHistoryDTO.getId());
      List<BdpColumnHistoryDTO> columnHistoryDTOS =
          metadataTableColumnHistoryService.selectList(columnHistoryDTO).stream()
              .sorted(Comparator.comparing(BdpColumnHistoryDTO::getOrderBy))
              .collect(Collectors.toList());
      tableHistoryDTO.setColumnHistoryList(columnHistoryDTOS);

      ColumnPartitionedHistoryDTO columnPartitionedHistoryDTO = new ColumnPartitionedHistoryDTO();
      columnPartitionedHistoryDTO.setTableId(tableHistoryDTO.getId());
      List<ColumnPartitionedHistoryDTO> partitionedHistoryDTOS =
          metadataTableColumnPartitionHistoryService
              .selectList(columnPartitionedHistoryDTO)
              .stream()
              .sorted(Comparator.comparing(ColumnPartitionedHistoryDTO::getOrderBy))
              .collect(Collectors.toList());
      tableHistoryDTO.setColumnPartitionedHistoryList(partitionedHistoryDTOS);
    }
  }

  public TableVersionCompareResponseVo compareWithVersion(
      String metadataId, String sourceVersion, String targetVersion) {
    BdpTableHistoryDTO query1 = new BdpTableHistoryDTO();
    query1.setTableId(metadataId);
    query1.setTableVersion(sourceVersion);
    BdpTableHistoryDTO logV1 = this.selectOne(query1);

    BdpTableHistoryDTO query2 = new BdpTableHistoryDTO();
    query2.setTableId(metadataId);
    if (targetVersion == null) {
      targetVersion = "";
    }
    query2.setTableVersion(targetVersion);
    BdpTableHistoryDTO logV2 = this.selectOne(query2);

    if (logV1 == null) {
      throw new DaoException("数据不存在");
    }

    if (logV2 == null) {
      logV2 = new BdpTableHistoryDTO();
    }

    return compare(logV1, logV2);
  }

  public TableVersionCompareResponseVo compare(String sourceVersionId, String targetVersionId) {
    BdpTableHistoryDTO logV1 = this.selectByPrimaryKey(sourceVersionId);
    BdpTableHistoryDTO logV2 = this.selectByPrimaryKey(targetVersionId);

    if (logV1 == null || logV2 == null) {
      throw new DaoException("数据不存在");
    }

    return compare(logV1, logV2);
  }

  public TableVersionCompareResponseVo compare(BdpTableHistoryDTO logV1, BdpTableHistoryDTO logV2) {
    TableVersionCompareResponseVo responseDTO = new TableVersionCompareResponseVo();

    compareTableProperties(responseDTO, logV1, logV2);

    compareColumn(responseDTO, logV1, logV2);

    return responseDTO;
  }

  private void compareTableProperties(
      TableVersionCompareResponseVo tableVersionCompare,
      BdpTableHistoryDTO logV1,
      BdpTableHistoryDTO logV2) {

    TablePropertiesCompareResponseVo propertiesCompareResult =
        new TablePropertiesCompareResponseVo();

    ValueCompareResponseDetail versionDetail = new ValueCompareResponseDetail();
    valueCompare(versionDetail, logV1.getTableVersion(), logV2.getTableVersion());
    propertiesCompareResult.setVersion(versionDetail);

    ValueCompareResponseDetail cnameDetail = new ValueCompareResponseDetail();
    String cNameV1 =
        metadataBusinessDao.getMetadataBusinessDetail(
            logV1.getTableId(), "cName", logV1.getTableVersion());
    String cNameV2 =
        metadataBusinessDao.getMetadataBusinessDetail(
            logV2.getTableId(), "cName", logV2.getTableVersion());

    valueCompare(cnameDetail, cNameV1, cNameV2);
    propertiesCompareResult.setCname(cnameDetail);

    ValueCompareResponseDetail nameDetail = new ValueCompareResponseDetail();
    valueCompare(nameDetail, logV1.getName(), logV2.getName());
    propertiesCompareResult.setName(nameDetail);

    // 主题域
    ValueCompareResponseDetail domainDetail = new ValueCompareResponseDetail();
    String domainV1 = null;
    String domainIdV1 =
        metadataBusinessDao.getMetadataBusinessDetail(
            logV1.getTableId(), "domain", logV1.getTableVersion());
    if (StringUtils.isNotBlank(domainIdV1)) {
      domainV1 = this.metadataTableDao.getMetadataDomain(domainIdV1);
    }

    String domainV2 = null;
    String domainIdV2 =
        metadataBusinessDao.getMetadataBusinessDetail(
            logV2.getTableId(), "domain", logV2.getTableVersion());
    if (StringUtils.isNotBlank(domainIdV1)) {
      domainV2 = this.metadataTableDao.getMetadataDomain(domainIdV2);
    }

    valueCompare(domainDetail, domainV1, domainV2);
    propertiesCompareResult.setDomain(domainDetail);

    // 分层
    ValueCompareResponseDetail dataHierarchyDetail = new ValueCompareResponseDetail();
    String dataHierarchyV1 = null;
    String dataHierarchyIdV1 =
        metadataBusinessDao.getMetadataBusinessDetail(
            logV1.getTableId(), "dataHierarchy", logV1.getTableVersion());
    if (StringUtils.isNotBlank(dataHierarchyIdV1)) {
      dataHierarchyV1 = this.metadataTableDao.getMetadataDataHierarchy(dataHierarchyIdV1);
    }

    String dataHierarchyV2 = null;
    String dataHierarchyIdV2 =
        metadataBusinessDao.getMetadataBusinessDetail(
            logV2.getTableId(), "dataHierarchy", logV2.getTableVersion());
    if (StringUtils.isNotBlank(domainIdV1)) {
      dataHierarchyV2 = this.metadataTableDao.getMetadataDataHierarchy(dataHierarchyIdV2);
    }
    valueCompare(dataHierarchyDetail, dataHierarchyV1, dataHierarchyV2);
    propertiesCompareResult.setDataHierarchy(dataHierarchyDetail);

    ValueCompareResponseDetail remarkDetail = new ValueCompareResponseDetail();
    valueCompare(remarkDetail, logV1.getRemark(), logV2.getRemark());
    propertiesCompareResult.setRemark(remarkDetail);

    tableVersionCompare.setPropertiesCompareResult(propertiesCompareResult);
  }

  private void compareColumn(
      TableVersionCompareResponseVo tableVersionCompare,
      BdpTableHistoryDTO logV1,
      BdpTableHistoryDTO logV2) {

    BdpColumnHistoryDTO columnHistoryDTO1 = new BdpColumnHistoryDTO();
    columnHistoryDTO1.setTableId(logV1.getId());
    List<BdpColumnHistoryDTO> columnHistoryListV1 =
        metadataTableColumnHistoryService.selectList(columnHistoryDTO1);
    if (CollectionUtils.isNotEmpty(columnHistoryListV1)) {
      for (BdpColumnHistoryDTO columnHistoryDTO : columnHistoryListV1) {
        columnHistoryDTO.setTableVersion(logV1.getTableVersion());
      }
    }

    BdpColumnHistoryDTO columnHistoryDTO2 = new BdpColumnHistoryDTO();
    if (logV2.getId() == null) {
      logV2.setId("");
    }
    columnHistoryDTO2.setTableId(logV2.getId());
    List<BdpColumnHistoryDTO> columnHistoryListV2 =
        metadataTableColumnHistoryService.selectList(columnHistoryDTO2);
    if (CollectionUtils.isNotEmpty(columnHistoryListV2)) {
      for (BdpColumnHistoryDTO columnHistoryDTO : columnHistoryListV2) {
        columnHistoryDTO.setTableVersion(logV2.getTableVersion());
      }
    }

    List<ColumnCompareResponseVo> columnComparisonResultList = new ArrayList<>();
    Set<String> columnNameSet = new HashSet<>();

    // v1列名集合
    Map<String, BdpColumnHistoryDTO> v1ColumnNameMap = new HashMap<>(16);
    for (BdpColumnHistoryDTO columnHistoryDTO : columnHistoryListV1) {
      v1ColumnNameMap.put(columnHistoryDTO.getName(), columnHistoryDTO);
      columnNameSet.add(columnHistoryDTO.getName());
    }

    // v2列名集合
    Map<String, BdpColumnHistoryDTO> v2ColumnNameMap = new HashMap<>(16);
    for (BdpColumnHistoryDTO columnHistoryDTO : columnHistoryListV2) {
      v2ColumnNameMap.put(columnHistoryDTO.getName(), columnHistoryDTO);
      columnNameSet.add(columnHistoryDTO.getName());
    }

    for (String columnName : columnNameSet) {
      ColumnCompareResponseVo columnCompareResult = new ColumnCompareResponseVo();

      BdpColumnHistoryDTO v1Column = v1ColumnNameMap.get(columnName);
      BdpColumnHistoryDTO v2Column = v2ColumnNameMap.get(columnName);

      v1Column = v1Column == null ? new BdpColumnHistoryDTO() : v1Column;
      v2Column = v2Column == null ? new BdpColumnHistoryDTO() : v2Column;

      ValueCompareResponseDetail tableVersionDetail = new ValueCompareResponseDetail();
      valueCompare(tableVersionDetail, v1Column.getTableVersion(), v2Column.getTableVersion());
      columnCompareResult.setTableVersion(tableVersionDetail);

      ValueCompareResponseDetail columnNameDetail = new ValueCompareResponseDetail();
      valueCompare(columnNameDetail, v1Column.getName(), v2Column.getName());
      columnCompareResult.setColumnName(columnNameDetail);

      ValueCompareResponseDetail columnCNameDetail = new ValueCompareResponseDetail();
      valueCompare(columnCNameDetail, v1Column.getCnName(), v2Column.getCnName());
      columnCompareResult.setColumnCName(columnCNameDetail);

      ValueCompareResponseDetail columnTypeDetail = new ValueCompareResponseDetail();
      valueCompare(columnTypeDetail, v1Column.getColumnType(), v2Column.getColumnType());
      columnCompareResult.setColumnType(columnTypeDetail);

      ValueCompareResponseDetail columnLengthDetail = new ValueCompareResponseDetail();
      valueCompare(columnLengthDetail, v1Column.getColumnLength(), v2Column.getColumnLength());
      columnCompareResult.setColumnLength(columnLengthDetail);

      ValueCompareResponseDetail precisionDetail = new ValueCompareResponseDetail();
      valueCompare(precisionDetail, v1Column.getColumnPrecision(), v2Column.getColumnPrecision());
      columnCompareResult.setColumnPrecision(precisionDetail);

      ValueCompareResponseDetail primaryKeyDetail = new ValueCompareResponseDetail();
      valueCompare(primaryKeyDetail, v1Column.getPrimaryKey(), v2Column.getPrimaryKey());
      columnCompareResult.setPrimaryKey(primaryKeyDetail);

      ValueCompareResponseDetail uniqueKeyDetail = new ValueCompareResponseDetail();
      valueCompare(uniqueKeyDetail, v1Column.getUniqueKey(), v2Column.getUniqueKey());
      columnCompareResult.setUnique(uniqueKeyDetail);

      ValueCompareResponseDetail nullableDetail = new ValueCompareResponseDetail();
      valueCompare(nullableDetail, v1Column.getNullable(), v2Column.getNullable());
      columnCompareResult.setNullable(nullableDetail);

      ValueCompareResponseDetail foreignKeyDetail = new ValueCompareResponseDetail();
      valueCompare(foreignKeyDetail, v1Column.getForeignKey(), v2Column.getForeignKey());
      columnCompareResult.setForeignKey(foreignKeyDetail);

      ValueCompareResponseDetail remarkDetail = new ValueCompareResponseDetail();
      valueCompare(remarkDetail, v1Column.getRemark(), v2Column.getRemark());
      columnCompareResult.setRemark(remarkDetail);

      columnComparisonResultList.add(columnCompareResult);
    }

    tableVersionCompare.setColumnComparisonResult(columnComparisonResultList);
  }

  private void valueCompare(ValueCompareResponseDetail detail, Object valueOfV1, Object valueOfV2) {
    detail.setValueOfVersion1(valueOfV1);
    detail.setValueOfVersion2(valueOfV2);
    if (valueOfV1 == null && valueOfV2 == null) {
      detail.setSame(true);
    } else if (valueOfV1 == null && valueOfV2 != null) {
      detail.setSame(false);
    } else if (valueOfV1 != null && valueOfV2 == null) {
      detail.setSame(false);
    } else {
      detail.setSame(Objects.equals(valueOfV1, valueOfV2));
    }
  }

  public int deleteByDatasourceId(String datasourceId) {
    return this.mapper.deleteByDatasourceId(datasourceId);
  }
}
